package io.kiki.stack.http.feign.SpringMvcContract;

import io.kiki.stack.http.feign.Feign;
import org.springframework.context.ApplicationContext;


public class FeignClientBuilder {

    private final ApplicationContext applicationContext;

    public FeignClientBuilder(final ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    public <T> Builder<T> forType(final Class<T> type, final String name) {
        return new Builder<>(this.applicationContext, type, name);
    }

    public <T> Builder<T> forType(final Class<T> type, final FeignClientFactoryBean clientFactoryBean, final String name) {
        return new Builder<>(this.applicationContext, clientFactoryBean, type, name);
    }

    /**
     * Builder of feign targets.
     *
     * @param <T> type of target
     */
    public static final class Builder<T> {

        private final FeignClientFactoryBean feignClientFactoryBean;

        private Builder(final ApplicationContext applicationContext, final Class<T> type, final String name) {
            this(applicationContext, new FeignClientFactoryBean(), type, name);
        }

        private Builder(final ApplicationContext applicationContext, final FeignClientFactoryBean clientFactoryBean, final Class<T> type, final String name) {
            this.feignClientFactoryBean = clientFactoryBean;

            this.feignClientFactoryBean.setApplicationContext(applicationContext);
            this.feignClientFactoryBean.setType(type);
            this.feignClientFactoryBean.setName(FeignClientsRegistrar.getName(name));
            this.feignClientFactoryBean.setContextId(FeignClientsRegistrar.getName(name));
            this.feignClientFactoryBean.setInheritParentContext(true);
            // preset default values - these values resemble the default values on the
            // FeignClient annotation
            this.url("").path("").dismiss404(false);
        }

        public Builder<T> url(final String url) {
            this.feignClientFactoryBean.setUrl(FeignClientsRegistrar.getUrl(url));
            return this;
        }

        /**
         * Applies a {@link FeignBuilderCustomizer} to the underlying
         * {@link Feign.Builder}. May be called multiple times.
         *
         * @param customizer applied in the same order as supplied here after applying
         *                   customizers found in the context.
         * @return the {@link Builder} with the customizer added
         */
        public Builder<T> customize(final FeignBuilderCustomizer customizer) {
            this.feignClientFactoryBean.addCustomizer(customizer);
            return this;
        }

        public Builder<T> contextId(final String contextId) {
            this.feignClientFactoryBean.setContextId(contextId);
            return this;
        }

        public Builder<T> path(final String path) {
            this.feignClientFactoryBean.setPath(FeignClientsRegistrar.getPath(path));
            return this;
        }

        public Builder<T> dismiss404(final boolean dismiss404) {
            this.feignClientFactoryBean.setDismiss404(dismiss404);
            return this;
        }

        public Builder<T> inheritParentContext(final boolean inheritParentContext) {
            this.feignClientFactoryBean.setInheritParentContext(inheritParentContext);
            return this;
        }

        public Builder<T> fallback(final Class<? extends T> fallback) {
            FeignClientsRegistrar.validateFallback(fallback);
            this.feignClientFactoryBean.setFallback(fallback);
            return this;
        }

        /**
         * @return the created Feign client
         */
        public T build() {
            return this.feignClientFactoryBean.getTarget();
        }

    }

}
